﻿#region usings
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using Wintellect.Sterling;
using Wintellect.Sterling.Database;
using Wintellect.Sterling.IsolatedStorage;
#endregion
namespace InsuranceAdjuster.OccasionallyConnected.ObjectStores {
    public class SterlingObjectStore : IObjectStore {
        #region Initialization
        private Object _SyncLock = new Object();
        private List<String> _myLog = new List<string>();
        private SterlingEngine _engine;
        private SterlingDefaultLogger _logger = null;
        private ISterlingDatabaseInstance _databaseInstance = null;
        #endregion
        public SterlingObjectStore() {
            _engine = new SterlingEngine();
            _engine.Activate();
            _logger = new SterlingDefaultLogger(SterlingLogLevel.Warning);
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) {
                if (isf.Quota < 20000000) {
                    isf.IncreaseQuotaTo(20000000);
                }
            }
            _databaseInstance = _engine.SterlingDatabase.RegisterDatabase<ObjectStoreDatabase>(new IsolatedStorageDriver());
        }

        #region Entity Storage

        public int Count {
            get {
                lock (_SyncLock) {
                    return _databaseInstance.Query<EntityContainer, Guid>().Count;
                }
            }
        }

        public void Clear() {
            _databaseInstance.Purge();
            RaisePropertyChanged("Count");
            RaisePropertyChanged("ClientAnchors");
        }

        public bool ContainsEntity(Guid entityGuid) {
            lock (_SyncLock) {
                return _databaseInstance.Query<EntityContainer, Guid>()
                                        .Any(x => x.Key == entityGuid);
            }
        }

        public bool ContainsEntity(IEntity entity) {
            return ContainsEntity(entity.EntityGuid);
        }

        public IEntity GetEntity(Guid entityGuid) {
            lock (_SyncLock) {
                return _databaseInstance.Query<EntityContainer, Guid>()
                                        .Where(x => x.Key == entityGuid)
                                        .Select(x => x.LazyValue)
                                        .FirstOrDefault().Value.Entity;
            }
        }

        public T GetEntity<T>(Guid entityGuid) where T : IEntity {
            lock (_SyncLock) {
                return (T)_databaseInstance.Query<EntityContainer, Guid>()
                                            .Where(x => x.Key == entityGuid)
                                            .FirstOrDefault().LazyValue.Value.Entity;
            }
        }

        public List<T> GetEntities<T>() where T : IEntity {
            lock (_SyncLock) {
                return _databaseInstance.Query<EntityContainer, Guid>()
                                        .Where(x => x.LazyValue.Value.Entity.GetType() == typeof(T))
                                        .Select(x => (T)x.LazyValue.Value.Entity)
                                        .ToList();
            }
        }

        public void SetEntity(IEntity entity) {
            lock (_SyncLock) {
                _databaseInstance.Save<EntityContainer, Guid>(new EntityContainer(entity));
                _databaseInstance.Flush();
            }
            RaisePropertyChanged("Count");
        }
        public void RemoveEntity(Guid entityGuid) {
            lock (_SyncLock) {
                var MyEntityContainer = _databaseInstance.Query<EntityContainer, Guid>()
                                                         .Where(x => x.Key == entityGuid)
                                                         .Select(x => x.LazyValue.Value)
                                                         .FirstOrDefault();
                if (MyEntityContainer == null)
                    return;
                
                _databaseInstance.Delete<EntityContainer>(MyEntityContainer);
                _databaseInstance.Flush();
            }

            RaisePropertyChanged("Count");
        }

        #endregion

        #region Durable Messaging

        public ObservableCollection<Message> GetDurableMessages() {
            lock (_SyncLock) {
                return _databaseInstance.Query<MessageContainer, Guid>()
                                        .Select(x => x.LazyValue.Value.Message)
                                        .ToObservableCollection();
            }
        }

        public bool IsMessagePersisted(Message message) {
            lock (_SyncLock) {
                return _databaseInstance.Query<MessageContainer, Guid>()
                                        .Any(x => x.Key == message.MessageGuid);
            }
        }

        public void UpdateDurableMessage(Message message) {
            if (message.IsDurable == false)
                return;

            lock (_SyncLock) {
                if (message.IsCompleted) {
                    _databaseInstance.Delete<MessageContainer>(new MessageContainer(message));
                    _databaseInstance.Flush();
                } else {
                    _databaseInstance.Save<MessageContainer>(new MessageContainer(message));
                    _databaseInstance.Flush();
                }
            }
        }

        #endregion

        #region Client Cache Anchors

        public Dictionary<String, DateTime> ClientAnchors {
            get {
                lock (_SyncLock) {
                    var Result = _databaseInstance.Query<AnchorItem, String>()
                                                  .Select(x => x.LazyValue.Value)
                                                  .ToDictionary(x => x.Table, x => x.Anchor);
                    _databaseInstance.Flush();
                    return Result;
                }
            }
        }

        public DateTime GetAnchor(String table) {
            lock (_SyncLock) {
                var MyAnchor = _databaseInstance.Query<AnchorItem, String>()
                                                .Where(x => x.Key.Equals(table, StringComparison.InvariantCultureIgnoreCase))
                                                .Select(x => x.LazyValue.Value)
                                                .FirstOrDefault();
                // For App First Start, anchor will be null, default to oldest date to get all values
                if (MyAnchor == null)
                    return DateTime.MinValue;
                else
                    return MyAnchor.Anchor;
            }
        }

        public void SetAnchor(String table, DateTime value) {
            lock (_SyncLock) {
                _databaseInstance.Save<AnchorItem>(new AnchorItem(table, value));
                _databaseInstance.Flush();
            }

            RaisePropertyChanged("ClientAnchors");
        }
        #endregion

        #region ObjectStoreDatabase Class

        public class ObjectStoreDatabase : BaseDatabaseInstance {
            public ObjectStoreDatabase() {
            }
            public override string Name {
                get { return "ObjectStoreDatabase"; }
            }
            protected override List<ITableDefinition> RegisterTables() {
                return new List<ITableDefinition>()
                {
                    CreateTableDefinition<EntityContainer, Guid>(x => x.EntityGuid).WithIndex<EntityContainer, String, Guid>("EntityType", x => x.EntityType),
                    CreateTableDefinition<MessageContainer, Guid>(x => x.MessageGuid),
                    CreateTableDefinition<AnchorItem, String>(x => x.Table)
                };
            }
        }

        #endregion

        #region EntityContainer Class

        public class EntityContainer {
            public IEntity Entity { get; set; }
            public Guid EntityGuid { get; set; }
            public DateTime EntityVersion { get; set; }
            public String EntityType { get; set; }

            public EntityContainer() {
            }
            public EntityContainer(IEntity entity) {
                this.Entity = entity;
                this.EntityGuid = entity.EntityGuid;
                this.EntityVersion = entity.EntityVersion;
                this.EntityType = entity.GetType().ToString();
            }
        }

        #endregion

        #region MessageContainer Class

        public class MessageContainer {
            public Guid MessageGuid { get; set; }
            public Message Message { get; set; }
            public MessageContainer() {
            }
            public MessageContainer(Message message) {
                Message = message;
                MessageGuid = message.MessageGuid;
            }
        }

        #endregion

        #region AnchorItem Class

        public class AnchorItem {
            public String Table { get; set; }
            public DateTime Anchor { get; set; }

            public AnchorItem() {
            }
            public AnchorItem(String table, DateTime anchor) {
                Table = table;
                Anchor = anchor;
            }
        }

        #endregion

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(String propertyName) {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
